Repository navigation
ena_maps: combine PSET non-spatial dims by coordinate label when projecting to a map - #3633
Conversation
…a map AbstractSkyMap.project_pset_values_to_map now adds each PSET's binned values into the map variable by non-spatial coordinate label instead of by array position, for both PUSH and PULL. PSETs may hold a subset of the map's labels, in any order. Mismatched dims, unknown labels, and size mismatches raise ValueError instead of silently broadcasting. The epoch dim is combined by position. Map variables can be pre-initialized with the full coords. Closes IMAP-Science-Operations-Center#3632 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Label-not-found error prints labels at full precision and notes that labels are matched exactly. - Positional size-mismatch error states why the dim is positional (no coordinate on map/PSET, or epoch). - Remove unreachable value_key check (KeyError check above covers it). - Fix value_keys type in project_pset_values_to_map docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Duplicate energy-step labels bypass validation, and coordinate-less dimensions gain artificial labels that cause incorrect reordering or unexpected errors during later accumulation.
2 open issues
What changed in this PR
This PR adds coordinate-label matching for non-spatial dimensions in ENA map projection, preparing the shared mapping code for Hi L2 gain-configuration combination without zero-padding each pointing set.
Changes:
- Aligns subset and reordered labels in PUSH and PULL projection, with positional handling for epoch and unlabeled dimensions.
- Supports pre-initialized map layouts and adds validation for incompatible dimensions and labels.
- Adds regression tests across rectangular and HEALPix maps, including masks, projection order, and error cases.
| File | Description |
|---|---|
imap_processing/tests/ena_maps/test_ena_maps.py |
Adds synthetic pointing-set fixtures and coordinate-label projection tests. |
imap_processing/ena_maps/ena_maps.py |
Adds label-resolution and accumulation helpers, updates initialization, and documents projection behavior. |
Review effort: Balanced
…uplicates - When creating a map variable, copy only real PSET coordinates. Dims without a coordinate no longer get artificial 0..N-1 map labels, so they stay combined by position. - Check for duplicate PSET labels before the identical-labels shortcut, so duplicates raise even when the map's labels came from the same PSET. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| f"Map variable '{value_key}' has dims {map_var.dims}; the last dim " | ||
| f"must be the spatial pixel dim '{pixel_dim}'." | ||
| ) | ||
| if set(pset_dims) != set(map_dims) or len(pset_dims) != len(map_dims): |
There was a problem hiding this comment.
why is the len() check needed here? On first look I don't think xarray allows for duplicate dim names.
There was a problem hiding this comment.
Good question. Claude says that:
xarray (2025.12.0) it only warns: "We do not yet support duplicate dimension names, but we do allow initial construction"
This can and has been updated to just a single test though.
| ) | ||
| if np.array_equal(pset_labels, map_labels): | ||
| return None | ||
| positions = map_var.indexes[dim].get_indexer(pset_labels) |
There was a problem hiding this comment.
Nice! Good to know about get_indexer().
There was a problem hiding this comment.
Yeah, I learned about that from this PR too 😃
| map_labels = map_var.coords[dim].values | ||
| # Check before the equality shortcut, so duplicates are caught even when | ||
| # the map's labels were copied from this PSET. | ||
| if len(np.unique(pset_labels)) != pset_size: |
There was a problem hiding this comment.
Is there a check anywhere that the map doesn't have duplicate labels?
There was a problem hiding this comment.
No, there isn't. Great catch! I'll add a check.

Change Summary
Overview
Why:
project_pset_values_to_mapmatched only the spatial axis to map pixels; non-spatial dims (e.g.gain_config_id,calibration_prod,esa_energy_step) were combined by array position, with the map variable shaped by the first PSET. A later PSET with different labels, a different order, or a length-1 dim was silently placed by numpy broadcasting — e.g. a PSET withgain_config_id=[1]was added into both gain-config slots of a 2-gain map. Hi L2 gain-config combination (#3619) hits this, and currently works around it by zero-padding every PSET to the full layout, which doubles the projection work and leaves the hazard in place for other callers.project_pset_values_to_mapnow combines PSET non-spatial dims into the map by coordinate label instead of array position (PUSH and PULL, rectangular and HEALPix maps). A PSET may hold a subset of the map variable's labels, in any order.ValueErrorinstead of silently broadcasting.epochis always combined by position (each PSET has its own epoch label), so Hi and Ultra L2 behavior is unchanged.data_1dwith the full non-spatial coords (pixel dim last). New variables reuse existing map coords for shared dims.Closes #3632. Adopting this in Hi L2 (#3619) is a follow-up.
Behavior changes
ValueError). No current caller relies on this, since map variables are created from the first PSET's shape.(*non_spatial, num_points)per value key per PSET (so it can share the slice-based add with PUSH). Negligible at current Hi/Ultra sizes.File changes
imap_processing/ena_maps/ena_maps.py: new_add_projected_values/_resolve_dim_positionshelpers used by both PUSH and PULL; map-variable creation honors existing map dims; PULL masks computed once per PSET; docstring describes label matching and pre-initialization; removed an unreachablevalue_keycheck and fixed thevalue_keysdocstring type.imap_processing/tests/ena_maps/test_ena_maps.py: newTestProjectByCoordinateLabelusing a small synthetic rectangular PSET.Testing
ValueErrorcase (including duplicate labels on the first PSET), coordinate-less dims staying positional, and a full-precision message for near-equal float labels. 21 of the original 25 fail on the old implementation.